package cn.edu.cug.cs.gtl.gsm.aof.ftp;

import cn.edu.cug.cs.gtl.common.Identifier;
import cn.edu.cug.cs.gtl.gsm.PaginationStorageManager;
import cn.edu.cug.cs.gtl.gsm.item.IndexItem;
import cn.edu.cug.cs.gtl.io.storage.StorageManager;

import java.io.*;
import java.util.ArrayList;

/**
 * @Author: jitao
 * @Date: 2022-07-03-15:00
 * @Description:
 */
public abstract class FTPBaseStorageManager extends PaginationStorageManager {

    /**
     * @param baseName
     * @param collectionSize
     * @param pageSize
     */
    public FTPBaseStorageManager(String baseName, long collectionSize, int pageSize) {
        super(baseName, collectionSize, pageSize);
    }

    @Override
    public void storeByteArray(Identifier pageIdentifier, byte[] data) throws IOException {
        if (pageIdentifier.longValue() != StorageManager.NEW_PAGE) {
            //如果是往已经存在的页面写入内容
            // 查找到指定的页面开头的Entry，直接删掉，由于是只添加模式的，删除后直接写入新的页面就行了，旧的页面直接弃用
            //当重新压缩文件的时候，才会真的删除这个弃页面。
            IndexItem oldEntry = this.pageMap.remove(pageIdentifier);
            if (oldEntry == null)
                throw new IOException("PaginationStorageManager.storeByteArray: Invalid Page Exception");
        }
        IndexItem e = new IndexItem(data.length,new ArrayList<>());

        int ptr = 0;//从data数组的第0个字节开始
        Identifier cPage = Identifier.create(StorageManager.NEW_PAGE);
        int cRem = data.length;
        int cLen = 0;

        while (cRem > 0) {
            cPage.reset(this.getNextPageIdentifier().longValue());
            this.getNextPageIdentifier().increase();
            cLen = (cRem > this.getPageSize()) ? this.getPageSize() : cRem;
            System.arraycopy(data, ptr, this.pageBuffer, 0, cLen);
            writeBufferToPage(cPage);
            ptr += cLen;
            cRem -= cLen;
            e.getPageIdentifiers().add((Identifier) cPage.clone());
        }
            pageIdentifier.copyFrom(e.getPageIdentifiers().get(0));
            this.pageMap.put((Identifier) pageIdentifier.clone(), e);
    }

    @Override
    public void deleteByteArray(Identifier pageIdentifier) throws IOException {
        IndexItem e = this.pageMap.remove(pageIdentifier);
        if (e == null)
            throw new IOException("PaginationStorageManager.deleteByteArray: " + pageIdentifier.toString());
    }

    @Override
    protected void readBufferFromPage(Identifier pageIdentifier) throws IOException {
        int order = this.metaItem.getCollectionOrder(pageIdentifier);
        long pos = pageIdentifier.longValue()*getPageSize() - getCollectionSize()*order*getPageSize();
        //需要进行文件切换
        if(order!=getCurrentCollectionOrder()){
//            getDataInputStream(getCurrentCollectionOrder()).close();
            setCurrentCollectionOrder(order);
        }
        DataInputStream inputStream = new DataInputStream(getDataInputStream(order));
        inputStream.skip(pos);
        inputStream.read(this.pageBuffer);
    }

    @Override
    protected void writeBufferToPage(Identifier pageIdentifier) throws IOException {
        int order = this.metaItem.getCollectionOrder(pageIdentifier);
        //需要进行文件切换
        if(order!=getCurrentCollectionOrder()){
            OutputStream dataOutputStream = getDataOutputStream(getCurrentCollectionOrder());
//            dataOutputStream.close();
            setCurrentCollectionOrder(order);
        }
        OutputStream dataOutputStream = getDataOutputStream(order);
        dataOutputStream.write(this.pageBuffer);
        dataOutputStream.flush();
    }

    @Override
    protected void readMetaFromStorage() throws IOException {
        DataInputStream dataInputStream = new DataInputStream(getIndexInputStream());
        int c = dataInputStream.readInt();
        if(c<=0) return;
        byte[] bs = new byte[c];
        dataInputStream.read(bs);
        this.metaItem.loadFromByteArray(bs);
    }

    @Override
    protected void writeMetaToStorage() throws IOException {
        byte[] bs = this.metaItem.storeToByteArray();
        DataOutputStream dataOutputStream = new DataOutputStream(getIndexOutputStream());
        dataOutputStream.writeInt(bs.length);
        dataOutputStream.write(bs);
    }

    @Override
    protected void readIndicesFromStorage() throws IOException {
        DataInputStream dataInputStream = new DataInputStream(getIndexInputStream());
        int c = dataInputStream.readInt();
        if(c<=0) return;
        byte[] bs = new byte[c];
        dataInputStream.read(bs);
        this.pageMap.loadFromByteArray(bs);
    }

    @Override
    protected void writeIndicesToStorage() throws IOException {
        DataOutputStream dataOutputStream = new DataOutputStream(getIndexOutputStream());
        byte[] bs = this.pageMap.storeToByteArray();
        dataOutputStream.writeInt(bs.length);
        dataOutputStream.write(bs);
        dataOutputStream.flush();
        dataOutputStream.close();
    }

    @Override
    protected void closeStorages() throws IOException {
        if (getIndexInputStream() != null) {
        getIndexOutputStream().close();
        }
        if (getIndexInputStream() != null) {
            getIndexInputStream().close();
        }
        if (getDataInputStream(this.currentCollectionOrder) != null) {
            getDataInputStream(this.currentCollectionOrder).close();
        }
        if (getDataOutputStream(this.currentCollectionOrder) != null) {
            getDataOutputStream(this.currentCollectionOrder).close();
        }
    }
    /**
     * 获取存储介质的Data文件输出流
     * @return
     */
    protected abstract OutputStream getDataOutputStream(int order);



    /**
     * 获取存储介质的Data文件输入流
     * @return
     */
    protected abstract InputStream getDataInputStream(int order);

    /**
     * 获取存储介质的Index文件输出流
     * @return
     */
    protected abstract OutputStream getIndexOutputStream();
    /**
     * 获取存储介质的Index文件输入流
     * @return
     */
    protected abstract InputStream getIndexInputStream();
}
